#include "triangles.h"
#include "alglsyst.h"

#include<fstream>
using std::ofstream;
using std::ostream;
using std::endl;
using std::ios;

ostream& operator<<(ostream& out,const Coord& some)
{
    out<<"("<<some.x<<","<<some.y<<")";
    return out;
}

ostream& operator<<(ostream& out,const Segment& some)
{
    out<<"S"<<some.c1<<","<<some.c2;
    return out;
}

ostream& operator<<(ostream& out,const Line& some)
{
    out<<"L("<<some.a<<","<<some.b<<","<<some.c<<")";
    return out;
}




double Periodic(double angle)
{
    double res=0;
    if(angle!=0)
    if(angle>0)
        res=angle-((int)(angle/(2*M_PI)))*2*M_PI;
    else
        res=((int)((-angle)/(2*M_PI))+1)*2*M_PI+angle;
    return res;
}
Coord Segment::InnerPoint(double Tau)
{
    Coord res=c1;
    res.x+=Tau*(c2.x-c1.x);
    res.y+=Tau*(c2.y-c1.y);
    return res;
}

double Segment::Length()
{
    return sqrt((c1.x-c2.x)*(c1.x-c2.x)+(c1.y-c2.y)*(c1.y-c2.y));
}

Segment Segment::Subsegment(double Partsize,double Deviation)
{
    double maxdev=(1-Partsize)/2;
    double subcen=maxdev*Deviation+0.5;
    double tau1=subcen-Partsize/2;
    double tau2=subcen+Partsize/2;
    return Segment(InnerPoint(tau1),InnerPoint(tau2));

}



Line::Line(Segment seg)
{
    if((seg.c1.x-seg.c2.x)!=0&&(seg.c1.y-seg.c2.y))
    {
        a=(seg.c2.y-seg.c1.y);
        b=(seg.c1.x-seg.c2.x);
        c=(seg.c2.y-seg.c1.y)*seg.c1.x-(seg.c2.x-seg.c1.x)*seg.c1.y;
    }
    else
        if((seg.c1.x-seg.c2.x)==0)
        {
            a=1;
            b=0;
            c=seg.c1.x;
        }
        else
            {
                a=0;
                b=1;
                c=seg.c1.y;
            }
}



Line::Line(Coord coo,double angle)
{
    SubConstr(coo,angle);
}

void Line::SubConstr(Coord coo,double angle)
{
    if(Periodic(angle)!=M_PI/2 && Periodic(angle)!=3*M_PI/2)
    {
        a=-tan(Periodic(angle));
        b=1;
        c=coo.y-tan(Periodic(angle))*coo.x;
    }
    else
    {
        a=1;
        b=0;
        c=coo.x;
    }

}


Line::Line(Segment seg,double Tau, double angle)
{
    double seg1x=seg.c1.x;
    double seg1y=seg.c1.y;
    double seg2x=seg.c2.x;
    double seg2y=seg.c2.y;
    double segipx=seg.InnerPoint(Tau).x;
    double segipy=seg.InnerPoint(Tau).y;
    SubConstr(seg.InnerPoint(Tau),Line(seg).Angle()+angle);
}

double Line::Angle()
{
    double res=M_PI/2;
    if(b!=0)
        res=atan(-a/b);
    return res;
}

double Line::Y(double x)
{
    double res=0;
    if(b!=0)
        res=(c-a*x)/b;
    return res;
}
double Line::X(double y)
{
    double res=0;
    if(a!=0)
        res=(c-b*y)/a;
    return res;
}

Coord Intersection(Line l1,Line l2)
{
    vector<vector<double> > matr(2,vector<double>(2,0));
    matr[0][0]=l1.a;
    matr[0][1]=l1.b;
    matr[1][0]=l2.a;
    matr[1][1]=l2.b;
    vector<double> rp(2,0);
    rp[0]=l1.c;
    rp[1]=l2.c;
    AlgebraicLinearSystem solver(matr,rp);
    vector<double> solve=GaussMeth(solver);
    return Coord(solve[0],solve[1]);
}

Triangle::Triangle(double Angle,Coord reppPoint, Segment Seg, int _width):c1(Seg.c1),c2(Seg.c2),width(_width)
{
    Segment corrseg(Seg);
    if(corrseg.c1.x>corrseg.c2.x)
    {
        corrseg.c1=Seg.c2;
        corrseg.c2=Seg.c1;
    }
    double length1=Segment(Intersection(Line(corrseg,0,Angle),Line(corrseg,1,M_PI-Angle)),reppPoint).Length();
    double length2=Segment(Intersection(Line(corrseg,0,-Angle),Line(corrseg,1,Angle-M_PI)),reppPoint).Length();
    if(length1>length2)
        c3=Intersection(Line(corrseg,0,Angle),Line(corrseg,1,M_PI-Angle));
    else
        c3=Intersection(Line(corrseg,0,-Angle),Line(corrseg,1,Angle-M_PI));

}
Coord Triangle::Center()
{
    Coord res(0,0);
    res.x=(c1.x+c2.x+c3.x)/3.0;
    res.y=(c1.y+c2.y+c3.y)/3.0;
    return res;
}
vector<Segment> Triangle::Borders()
{
    vector<Segment> res;
    res.reserve(2);
    res.push_back(Segment(c2,c3));
    res.push_back(Segment(c3,c1));
    return res;
}
vector<Segment> Triangle::FullBorders()
{
    vector<Segment> res;
    res.reserve(3);
    res.push_back(Segment(c1,c2));
    res.push_back(Segment(c2,c3));
    res.push_back(Segment(c3,c1));
    return res;
}



vector<Triangle> Fractal(int width,int height)
{
    double gAngles[7]={0.37,0.31,0.26,0.2,0.2,0.15,0.2};
    double gParts[7]={0.8,0.7,0.8,0.9,0.9,0.9,1};
    double gDeviat[7]={0.6,0.7,0.8,0.9,-1,1,0};
    vector<Triangle> res;
    res.reserve(1);
    res.push_back(Triangle(M_PI/3,Coord(0,0),Segment(Coord(width/2-100,height/2-33),Coord(width/2+100,height/2-33)),3));
//    res.push_back(Triangle(M_PI/3,Coord(0,height),Segment(Coord(width/2-55,height/2),Coord(width/2+115,height/2)),3));
    vector<Triangle> subres=res;
    vector<Triangle> subresN;
    ofstream anglesdata("angles.data",ios::out);
    for(unsigned int generation=0;generation<7;generation++)
    {
        anglesdata<<(M_PI/2-1/(generation-6/M_PI))*90.0/M_PI<<endl;
        subresN.reserve(res.size()*2);
        for(unsigned int i=0;i<subres.size();i++)
        {
            int wid=1;
            if(generation<3)
                wid=2;
            Coord centre=subres[i].Center();
            vector<Segment> bord=subres[i].Borders();
            if(generation==0)
            {
                bord=subres[i].FullBorders();
            }
            for(unsigned int u=0;u<bord.size();u++)
                subresN.push_back(Triangle(gAngles[generation]*M_PI,Coord(centre),bord[u].Subsegment(gParts[generation],gDeviat[generation]),wid));
        }
        res.reserve(res.size()+subresN.size());
        res.insert(res.end(),subresN.begin(),subresN.end());
        subres=subresN;
        subresN.clear();

    }
    anglesdata.close();

    return res;
}
